package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// 全局命令码映射，消息类型名称 -> 命令码
var MessageCmdMap = make(map[string]int32)

// ProtoCmdCodes 存储所有从proto文件中读取的命令码
type ProtoCmdCodes struct {
	// 心跳相关
	CmdHeartbeatReq  int32 // 心跳请求命令码
	CmdHeartbeatResp int32 // 心跳响应命令码

	// 登录相关
	CmdLoginReq  int32 // 登录请求命令码
	CmdLoginResp int32 // 登录响应命令码
}

// 全局命令码映射
var ProtoCmds ProtoCmdCodes

// 初始化proto命令码
func init() {
	// 先解析所有proto文件中的命令码到MessageCmdMap
	protoRoot := "./proto"
	MessageCmdMap = parseProtoCmdFromFiles(protoRoot)

	// 打印所有找到的命令码
	fmt.Println("所有Proto命令码映射:")
	for msgName, cmdVal := range MessageCmdMap {
		fmt.Printf("  %s: %d\n", msgName, cmdVal)
	}

	// 然后初始化ProtoCmds
	ProtoCmds = initProtoCmdCodes()
}

// 从proto文件中初始化命令码
func initProtoCmdCodes() ProtoCmdCodes {
	codes := ProtoCmdCodes{}

	// 心跳请求命令码
	if cmd, ok := MessageCmdMap["ReqHeartbeatMessage"]; ok {
		codes.CmdHeartbeatReq = cmd
		log.Printf("Read HeartbeatReq cmd from proto file: %d", cmd)
	} else {
		codes.CmdHeartbeatReq = 100001 // 默认值
		log.Printf("Using default HeartbeatReq cmd: %d", codes.CmdHeartbeatReq)
	}

	// 心跳响应命令码
	if cmd, ok := MessageCmdMap["ResHeartbeatMessage"]; ok {
		codes.CmdHeartbeatResp = cmd
		log.Printf("Read HeartbeatResp cmd from proto file: %d", cmd)
	} else {
		codes.CmdHeartbeatResp = -100001 // 默认值
		log.Printf("Using default HeartbeatResp cmd: %d", codes.CmdHeartbeatResp)
	}

	// 登录请求命令码
	if cmd, ok := MessageCmdMap["ReqLoginMessage"]; ok {
		codes.CmdLoginReq = cmd
		log.Printf("Read LoginReq cmd from proto file: %d", cmd)
	} else {
		codes.CmdLoginReq = 100101 // 默认值
		log.Printf("Using default LoginReq cmd: %d", codes.CmdLoginReq)
	}

	// 登录响应命令码
	if cmd, ok := MessageCmdMap["ResLoginMessage"]; ok {
		codes.CmdLoginResp = cmd
		log.Printf("Read LoginResp cmd from proto file: %d", cmd)
	} else {
		codes.CmdLoginResp = -100101 // 默认值
		log.Printf("Using default LoginResp cmd: %d", codes.CmdLoginResp)
	}

	// 打印所有读取到的命令码
	fmt.Printf("Loaded command codes from proto files: \n")
	fmt.Printf("  - HeartbeatReq:  %d\n", codes.CmdHeartbeatReq)
	fmt.Printf("  - HeartbeatResp: %d\n", codes.CmdHeartbeatResp)
	fmt.Printf("  - LoginReq:      %d\n", codes.CmdLoginReq)
	fmt.Printf("  - LoginResp:     %d\n", codes.CmdLoginResp)

	return codes
}

// 从proto文件夹中解析所有命令码
func parseProtoCmdFromFiles(rootDir string) map[string]int32 {
	result := make(map[string]int32)

	// 遍历proto目录下的所有.proto文件
	err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && strings.HasSuffix(info.Name(), ".proto") {
			parseProtoCmdFromFile(path, result)
		}
		return nil
	})

	if err != nil {
		log.Printf("Error walking proto directory: %v", err)
	}

	return result
}

// 从单个proto文件中解析命令码
func parseProtoCmdFromFile(filePath string, result map[string]int32) {
	// 读取文件内容
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		log.Printf("Error reading proto file %s: %v", filePath, err)
		return
	}

	fileContent := string(content)

	// 定义正则表达式来匹配message定义和cmd选项
	msgRegex := regexp.MustCompile(`message\s+([A-Za-z0-9_]+)\s*\{`)
	cmdRegex := regexp.MustCompile(`option\s*\(com\.lin\.proto\.cmd\)\s*=\s*(-?\d+)`)

	// 查找所有message定义
	msgMatches := msgRegex.FindAllStringSubmatchIndex(fileContent, -1)
	for _, msgMatch := range msgMatches {
		if len(msgMatch) >= 4 {
			// 获取message名称
			msgName := fileContent[msgMatch[2]:msgMatch[3]]

			// 查找message内容的结束位置（简化处理，可能不准确）
			msgEndPos := strings.Index(fileContent[msgMatch[0]:], "}")
			if msgEndPos == -1 {
				continue
			}

			// 提取message内容
			msgContent := fileContent[msgMatch[0] : msgMatch[0]+msgEndPos]

			// 在message内容中查找cmd选项
			cmdMatch := cmdRegex.FindStringSubmatch(msgContent)
			if len(cmdMatch) >= 2 {
				// 解析命令码值
				cmdVal, err := strconv.ParseInt(cmdMatch[1], 10, 32)
				if err != nil {
					log.Printf("Error parsing cmd value in %s: %v", msgName, err)
					continue
				}

				// 保存命令码
				result[msgName] = int32(cmdVal)
				log.Printf("Found cmd %d for message %s in file %s", cmdVal, msgName, filePath)
			}
		}
	}
}

// 获取proto消息的命令码 - 使用map查找，更加灵活
func getProtoCommandCode(msg interface{}) (int32, bool) {
	// 基于反射获取消息类型名称
	msgType := reflect.TypeOf(msg).Elem().Name()

	// 直接从映射表中查找命令码
	if cmd, ok := MessageCmdMap[msgType]; ok {
		return cmd, true
	}

	// 找不到命令码
	return 0, false
}
